package cn.zwx.common.sentinel.handler;

import cn.zwx.base.entity.ResponseEntity;
import cn.zwx.base.enums.ResponseCodeEnum;
import cn.zwx.base.exception.BizWrapException;
import cn.zwx.base.utils.json.JsonUtils;
import cn.zwx.common.sentinel.properties.ZwxSentinePropreties;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhangwenxue
 */
public class BlockExceptionHandler {
    public static final Logger logger = LoggerFactory.getLogger(BlockExceptionHandler.class);

    private final ZwxSentinePropreties propreties;

    public BlockExceptionHandler(ZwxSentinePropreties propreties) {
        this.propreties = propreties;
    }

    public  void processBlockException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Exception e) throws IOException {
        ResponseEntity<Void> responseEntity = getVoidResponseEntity(httpServletRequest, e);
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        // spring mvc自带的json操作工具，叫jackson
        logger.error(JSON.toJSONString(responseEntity));
        new ObjectMapper()
                .writeValue(
                        httpServletResponse.getWriter(),
                        responseEntity
                );
    }

    public ResponseEntity<Void> getVoidResponseEntity(HttpServletRequest httpServletRequest, Throwable e) {
        ResponseEntity<Void> responseEntity;
        String requestUri = httpServletRequest.getRequestURI();
        this.recordExceptionToSentinel(e);
        if (e instanceof FlowException) {
            logger.error(requestUri+"接口限流了!", e);
            responseEntity = ResponseEntity.fail(ResponseCodeEnum.PERIOD_LIMITER_ERROR);
        } else if (e instanceof DegradeException) {
            logger.error(propreties.getAppId()+"服务降级了！", e);
            responseEntity = ResponseEntity.fail(ResponseCodeEnum.SERVICE_NOT_PERMITTED_ERROR);
        } else if (e instanceof ParamFlowException) {
            logger.error(requestUri+"接口热点参数限流了!", e);
            responseEntity = ResponseEntity.fail(ResponseCodeEnum.PERIOD_LIMITER_ERROR);
        } else if (e instanceof SystemBlockException) {
            logger.error(propreties.getAppId()+"统规则（负载/...不满足要求）！", e);
            responseEntity = ResponseEntity.fail(ResponseCodeEnum.SERVICE_RESOURCE_LIMIT_ERROR);
        } else if (e instanceof AuthorityException) {
            logger.error(propreties.getAppId()+"授权规则不通过！", e);
            responseEntity = ResponseEntity.fail(ResponseCodeEnum.SERVICE_PERMISSION_ERROR);
        }else if (e instanceof BizWrapException){
            BizWrapException  bizException = (BizWrapException) e;
            responseEntity = ResponseEntity.fail(bizException.getCode(),bizException.getMsg());
        }else if (e instanceof IllegalArgumentException){
            responseEntity =   ResponseEntity.fail(ResponseCodeEnum.REQUEST_PARMA_ERROR.getCode(), e.getMessage());
        }else if (e instanceof HttpMessageNotReadableException){
            responseEntity =  ResponseEntity.fail(ResponseCodeEnum.PARAM_PARSE_ERROR.getCode(), e.getMessage());
        }else if ( e instanceof MethodArgumentNotValidException){
            MethodArgumentNotValidException ex =(MethodArgumentNotValidException) e;
            Map<String, String> response = new HashMap<>();
            ex.getBindingResult().getAllErrors().forEach((error) -> {
                String fieldName = ((FieldError) error).getField();
                String message = error.getDefaultMessage();
                response.put(fieldName, message);
            });
            return ResponseEntity.fail(ResponseCodeEnum.REQUEST_PARMA_ERROR.getCode(), JsonUtils.toJsonString(response));
        }else {
            // http状态码
            logger.error(requestUri+"未知异常!", e);
            responseEntity = ResponseEntity.fail(ResponseCodeEnum.UNKNOWN_ERROR);
        }
        return responseEntity;
    }

    private void recordExceptionToSentinel(Throwable e)  {
         if(e instanceof IOException){
            Tracer.trace(e);
        }else if(e instanceof ServletException){
            Tracer.trace(e);
        }else if(e instanceof RuntimeException){
            Tracer.trace(e);
        }else if(e instanceof BizWrapException){
             Tracer.trace(e);
         }
    }
}
